home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 151-175 / disk_166 / stevie / source / normal.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  34KB  |  1,738 lines

  1. /*
  2.  * STEVIE - Simply Try this Editor for VI Enthusiasts
  3.  *
  4.  * Code Contributions By : Tim Thompson           twitch!tjt
  5.  *                         Tony Andrews           onecom!wldrdg!tony 
  6.  *                         G. R. (Fred) Walter    watmath!watcgl!grwalter 
  7.  */
  8.  
  9. /*
  10.  * This file contains the main routine for processing characters in command
  11.  * mode as well as routines for handling the operators. 
  12.  */
  13.  
  14. #include "stevie.h"
  15.  
  16. static void
  17. doshift(), dodelete(), doput(), dochange();
  18. static void
  19.                 startinsert();
  20. static          bool_t
  21.                 dojoin();
  22. static          bool_t
  23.                 doyank();
  24.  
  25. /*
  26.  * Macro evaluates true if char 'c' is a valid identifier character 
  27.  */
  28. #define    IDCHAR(c)    (isalpha(c) || isdigit(c) || (c) == '_')
  29.  
  30. /*
  31.  * Operators 
  32.  */
  33. #define    NOP    0        /* no pending operation */
  34. #define    DELETE    1
  35. #define    YANK    2
  36. #define    CHANGE    3
  37. #define    LSHIFT    4
  38. #define    RSHIFT    5
  39.  
  40. #define    CLEAROP    (operator = NOP)/* clear any pending operator */
  41.  
  42. static int      operator = NOP;    /* current pending operator */
  43.  
  44. /*
  45.  * When a cursor motion command is made, it is marked as being a character or
  46.  * line oriented motion. Then, if an operator is in effect, the operation
  47.  * becomes character or line oriented accordingly. 
  48.  *
  49.  * Character motions are marked as being inclusive or not. Most char. motions
  50.  * are inclusive, but some (e.g. 'w') are not. 
  51.  *
  52.  * Generally speaking, every command in normal() should either clear any pending
  53.  * operator (with CLEAROP), or set the motion type variable. 
  54.  */
  55.  
  56. /*
  57.  * Motion types 
  58.  */
  59. #define    MBAD    (-1)        /* 'bad' motion type marks unusable yank buf */
  60. #define    MCHAR    0
  61. #define    MLINE    1
  62.  
  63. static int      mtype;        /* type of the current cursor motion */
  64. static bool_t   mincl;        /* true if char motion is inclusive */
  65. static int      ybtype = MBAD;
  66. static int      ybcrossline = FALSE;
  67.  
  68. static LPTR     startop;    /* cursor pos. at start of operator */
  69.  
  70. /*
  71.  * Operators can have counts either before the operator, or between the
  72.  * operator and the following cursor motion as in: 
  73.  *
  74.  * d3w or 3dw 
  75.  *
  76.  * If a count is given before the operator, it is saved in opnum. If normal() is
  77.  * called with a pending operator, the count in opnum (if present) overrides
  78.  * any count that came later. 
  79.  */
  80. static int      opnum = 0;
  81.  
  82. #define    DEFAULT1(x)    (((x) == 0) ? 1 : (x))
  83.  
  84. /*
  85.  * normal 
  86.  *
  87.  * Execute a command in normal mode. 
  88.  */
  89.  
  90. void
  91. normal(c)
  92.     char            c;
  93. {
  94.     char           *p;
  95.     int             n;
  96.     int             nn;
  97.     bool_t          flag = FALSE;
  98.     int             type = 0;    /* used in some operations to modify type */
  99.     int             dir = FORWARD;    /* search direction */
  100.     char            nchar = NUL;
  101.     bool_t          finish_op;
  102.     LPTR            temp_Curschar;
  103.  
  104.     last_command = NUL;
  105.     /*
  106.      * If there is an operator pending, then the command we take this time
  107.      * will terminate it. Finish_op tells us to finish the operation before
  108.      * returning this time (unless the operation was cancelled). 
  109.      */
  110.     finish_op = (operator != NOP);
  111.  
  112.     /*
  113.      * If we're in the middle of an operator AND we had a count before the
  114.      * operator, then that count overrides the current value of Prenum. What
  115.      * this means effectively, is that commands like "3dw" get turned into
  116.      * "d3w" which makes things fall into place pretty neatly. 
  117.      */
  118.     if (finish_op) {
  119.     if (opnum != 0)
  120.         Prenum = opnum;
  121.     } else
  122.     opnum = 0;
  123.  
  124.     switch (c) {
  125.  
  126.       case K_HELP:
  127.     CLEAROP;
  128.     if (help()) {
  129.         screenclear();
  130.         updateNextscreen();
  131.     }
  132.     break;
  133.  
  134.       case CTRL('L'):
  135.     CLEAROP;
  136.     screenclear();
  137.     updateNextscreen();
  138.     break;
  139.  
  140.       case CTRL('D'):
  141.     CLEAROP;
  142.     if (Prenum)
  143.         P(P_SS) = (Prenum > Rows - 1) ? Rows - 1 : Prenum;
  144.     scrollup((P(P_SS) < Rows) ? P(P_SS) : Rows - 1);
  145.     onedown((P(P_SS) < Rows) ? P(P_SS) : Rows - 1);
  146.     updateNextscreen();
  147.     break;
  148.  
  149.       case CTRL('U'):
  150.     CLEAROP;
  151.     if (Prenum)
  152.         P(P_SS) = (Prenum > Rows - 1) ? Rows - 1 : Prenum;
  153.     scrolldown((P(P_SS) < Rows) ? P(P_SS) : Rows - 1);
  154.     oneup((P(P_SS) < Rows) ? P(P_SS) : Rows - 1);
  155.     updateNextscreen();
  156.     break;
  157.  
  158.       case CTRL('F'):
  159.     CLEAROP;
  160.     if (nextline(Topchar) == NULL) {
  161.         beep();
  162.         break;
  163.     }
  164.     screenclear();
  165.     Prenum = DEFAULT1(Prenum);
  166.     while (Prenum > 0) {
  167.         *Curschar = *prevline(Botchar);
  168.         *Topchar = *Curschar;
  169.         Topchar->index = 0;
  170.         updateNextscreen();
  171.         Prenum--;
  172.     }
  173.     beginline(TRUE);
  174.     break;
  175.  
  176.       case CTRL('B'):
  177.     CLEAROP;
  178.     if (prevline(Topchar) == NULL) {
  179.         beep();
  180.         break;
  181.     }
  182.     screenclear();
  183.     Prenum = DEFAULT1(Prenum);
  184.     while (Prenum > 0) {
  185.         *Curschar = *Topchar;
  186.         n = Rows - 1;
  187.         {
  188.         LPTR           *lp = Curschar;
  189.         int             l = 0;
  190.  
  191.         while ((l < n) && (lp != NULL)) {
  192.             l += plines(lp);
  193.             *Topchar = *lp;
  194.             lp = prevline(lp);
  195.         }
  196.         }
  197.         Topchar->index = 0;
  198.         Prenum--;
  199.     }
  200.     beginline(TRUE);
  201.     updateNextscreen();
  202.     break;
  203.  
  204.       case CTRL('E'):
  205.     CLEAROP;
  206.     scrollup(DEFAULT1(Prenum));
  207.     updateNextscreen();
  208.     break;
  209.  
  210.       case CTRL('Y'):
  211.     CLEAROP;
  212.     scrolldown(DEFAULT1(Prenum));
  213.     updateNextscreen();
  214.     break;
  215.  
  216.       case 'z':
  217.     CLEAROP;
  218.     switch (vgetc()) {
  219.       case NL:        /* put Curschar at top of screen */
  220.       case CR:
  221.         *Topchar = *Curschar;
  222.         Topchar->index = 0;
  223.         updateNextscreen();
  224.         break;
  225.  
  226.       case '.':        /* put Curschar in middle of screen */
  227.         n = Rows / 2;
  228.         goto dozcmd;
  229.  
  230.       case '-':        /* put Curschar at bottom of screen */
  231.         n = Rows - 1;
  232.         /* FALLTHROUGH */
  233.  
  234.     dozcmd:
  235.         {
  236.         LPTR           *lp = Curschar;
  237.         int             l = 0;
  238.  
  239.         while ((l < n) && (lp != NULL)) {
  240.             l += plines(lp);
  241.             *Topchar = *lp;
  242.             lp = prevline(lp);
  243.         }
  244.         }
  245.         Topchar->index = 0;
  246.         updateNextscreen();
  247.         break;
  248.  
  249.       default:
  250.         beep();
  251.     }
  252.     break;
  253.  
  254.       case CTRL('G'):
  255.     CLEAROP;
  256.     fileinfo();
  257.     break;
  258.  
  259.       case 'G':
  260.     mtype = MLINE;
  261.     *Curschar = *gotoline(Prenum);
  262.     beginline(TRUE);
  263.     break;
  264.  
  265.       case 'H':
  266.     mtype = MLINE;
  267.     *Curschar = *Topchar;
  268.     for (n = Prenum; n && onedown(1); n--);
  269.     beginline(TRUE);
  270.     break;
  271.  
  272.       case 'M':
  273.     mtype = MLINE;
  274.     *Curschar = *Topchar;
  275.     for (n = 0; n < Rows / 2 && onedown(1); n++);
  276.     beginline(TRUE);
  277.     break;
  278.  
  279.       case 'L':
  280.     mtype = MLINE;
  281.     *Curschar = *prevline(Botchar);
  282.     for (n = Prenum; n && oneup(1); n--);
  283.     beginline(TRUE);
  284.     break;
  285.  
  286.       case 'l':
  287.       case K_RARROW:
  288.       case ' ':
  289.     mtype = MCHAR;
  290.     mincl = FALSE;
  291.     n = DEFAULT1(Prenum);
  292.     while (n--) {
  293.         if (!oneright()) {
  294.         if (operator != DELETE && operator != CHANGE) {
  295.             beep();
  296.         } else {
  297.             if (lineempty(Curschar)) {
  298.             CLEAROP;
  299.             beep();
  300.             } else {
  301.             mincl = TRUE;
  302.             }
  303.         }
  304.         break;
  305.         }
  306.     }
  307.     set_want_col = TRUE;
  308.     break;
  309.  
  310.       case 'h':
  311.       case K_LARROW:
  312.       case CTRL('H'):
  313.     mtype = MCHAR;
  314.     mincl = FALSE;
  315.     Prenum = DEFAULT1(Prenum);
  316.     n = Prenum;
  317.     while (n--) {
  318.         if (!oneleft()) {
  319.         if (operator != DELETE && operator != CHANGE) {
  320.             beep();
  321.         } else if (Prenum == 1) {
  322.             CLEAROP;
  323.             beep();
  324.         }
  325.         break;
  326.         }
  327.     }
  328.     set_want_col = TRUE;
  329.     break;
  330.  
  331.       case '-':
  332.     flag = TRUE;
  333.     /* FALLTHROUGH */
  334.  
  335.       case 'k':
  336.       case K_UARROW:
  337.       case CTRL('P'):
  338.     mtype = MLINE;
  339.     if (!oneup(DEFAULT1(Prenum))) {
  340.         CLEAROP;
  341.         beep();
  342.     } else if (flag)
  343.         beginline(TRUE);
  344.     break;
  345.  
  346.       case '+':
  347.       case CR:
  348.       case NL:
  349.     flag = TRUE;
  350.     /* FALLTHROUGH */
  351.  
  352.       case 'j':
  353.       case K_DARROW:
  354.       case CTRL('N'):
  355.     mtype = MLINE;
  356.     if (!onedown(DEFAULT1(Prenum))) {
  357.         CLEAROP;
  358.         beep();
  359.     } else if (flag)
  360.         beginline(TRUE);
  361.     break;
  362.  
  363.     /*
  364.      * This is a strange motion command that helps make operators more
  365.      * logical. It is actually implemented, but not documented in the
  366.      * real 'vi'. This motion command actually refers to "the current
  367.      * line". Commands like "dd" and "yy" are really an alternate form of
  368.      * "d_" and "y_". It does accept a count, so "d3_" works to delete 3
  369.      * lines. 
  370.      */
  371.       case '_':
  372. lineop:
  373.     mtype = MLINE;
  374.     if (!onedown(DEFAULT1(Prenum) - 1)) {
  375.         CLEAROP;
  376.         beep();
  377.     } else
  378.         beginline(TRUE);
  379.     break;
  380.  
  381.       case '|':
  382.     mtype = MCHAR;
  383.     mincl = TRUE;
  384.     beginline(FALSE);
  385.     if (Prenum > 0)
  386.         *Curschar = *coladvance(Curschar, Prenum - 1);
  387.     Curswant = Prenum - 1;
  388.     break;
  389.  
  390.       case CTRL(']'):        /* :ta to current identifier */
  391.     CLEAROP;
  392.     {
  393.         char            ch;
  394.         LPTR            save;
  395.  
  396.         save = *Curschar;
  397.         /*
  398.          * First back up to start of identifier. This doesn't match the
  399.          * real vi but I like it a little better and it shouldn't bother
  400.          * anyone. 
  401.          */
  402.         ch = gchar(Curschar);
  403.         while (IDCHAR(ch)) {
  404.         if (!oneleft())
  405.             break;
  406.         ch = gchar(Curschar);
  407.         }
  408.         if (!IDCHAR(ch))
  409.         oneright();
  410.  
  411.         stuffReadbuff(":ta ");
  412.         /*
  413.          * Now grab the chars in the identifier 
  414.          */
  415.         ch = gchar(Curschar);
  416.         while (IDCHAR(ch)) {
  417.         stuffReadbuff(mkstr(ch));
  418.         if (!oneright())
  419.             break;
  420.         ch = gchar(Curschar);
  421.         }
  422.         stuffReadbuff("\n");
  423.  
  424.         *Curschar = save;    /* restore, in case of error */
  425.     }
  426.     break;
  427.  
  428.       case '%':
  429.     mtype = MCHAR;
  430.     mincl = TRUE;
  431.     {
  432.         LPTR           *pos;
  433.  
  434.         if ((pos = showmatch()) == NULL) {
  435.         CLEAROP;
  436.         beep();
  437.         } else {
  438.         setpcmark();
  439.         *Curschar = *pos;
  440.         set_want_col = TRUE;
  441.         }
  442.     }
  443.     break;
  444.  
  445.     /*
  446.      * Word Motions 
  447.      */
  448.  
  449.       case 'B':
  450.     type = 1;
  451.     /* FALLTHROUGH */
  452.  
  453.       case 'b':
  454.     mtype = MCHAR;
  455.     mincl = FALSE;
  456.     set_want_col = TRUE;
  457.     for (n = DEFAULT1(Prenum); n > 0; n--) {
  458.         LPTR           *pos;
  459.  
  460.         if ((Curschar->linep->prev == NULL) && (Curschar->index == 0)) {
  461.         CLEAROP;
  462.         beep();
  463.         break;
  464.         }
  465.         pos = bck_word(Curschar, type);
  466.         if (pos == NULL)
  467.         *Curschar = *gotoline(1);    /* goto top of file */
  468.         else
  469.         *Curschar = *pos;
  470.     }
  471.     break;
  472.  
  473.       case 'W':
  474.     type = 1;
  475.     /* FALLTHROUGH */
  476.  
  477.       case 'w':
  478.     /*
  479.      * This is a little strange. To match what the real vi does, we
  480.      * effectively map 'cw' to 'ce', and 'cW' to 'cE'. This seems
  481.      * impolite at first, but it's really more what we mean when we say
  482.      * 'cw'. 
  483.      */
  484.     if (operator == CHANGE)
  485.         goto doecmd;
  486.  
  487.     mtype = MCHAR;
  488.     mincl = FALSE;
  489.     set_want_col = TRUE;
  490.     for (n = DEFAULT1(Prenum); n > 0; n--) {
  491.         LPTR           *pos;
  492.  
  493.         if ((pos = fwd_word(Curschar, type)) == NULL) {
  494.         CLEAROP;
  495.         beep();
  496.         break;
  497.         } else
  498.         *Curschar = *pos;
  499.     }
  500.     break;
  501.  
  502.       case 'E':
  503.     type = 1;
  504.     /* FALLTHROUGH */
  505.  
  506.       case 'e':
  507. doecmd:
  508.     mtype = MCHAR;
  509.     mincl = TRUE;
  510.     set_want_col = TRUE;
  511.     for (n = DEFAULT1(Prenum); n > 0; n--) {
  512.         LPTR           *pos;
  513.  
  514.         if ((pos = end_word(Curschar, type)) == NULL) {
  515.         CLEAROP;
  516.         beep();
  517.         break;
  518.         } else
  519.         *Curschar = *pos;
  520.     }
  521.     break;
  522.  
  523.       case '$':
  524.     mtype = MCHAR;
  525.     mincl = TRUE;
  526.     while (oneright());
  527.     Curswant = 999;        /* so we stay at the end */
  528.     break;
  529.  
  530.       case '^':
  531.     flag = TRUE;
  532.     /* FALLTHROUGH */
  533.  
  534.       case '0':
  535.     mtype = MCHAR;
  536.     mincl = TRUE;
  537.     beginline(flag);
  538.     break;
  539.  
  540.       case 'A':
  541.     set_want_col = TRUE;
  542.     while (oneright());
  543.     ResetBuffers();
  544.     AppendToRedobuff("A");
  545.     goto doAPPENDcmd;
  546.  
  547.       case 'a':
  548.     ResetBuffers();
  549.     AppendToRedobuff("a");
  550.  
  551. doAPPENDcmd:
  552.     CLEAROP;
  553.     /* Works just like an 'i'nsert on the next character. */
  554.     n = RowNumber(Curschar);
  555.     AppendPositionToUndoUndobuff(Curschar->index, n);
  556.     AppendToUndoUndobuff("a");
  557.  
  558.     if (!lineempty(Curschar))
  559.         inc(Curschar);
  560.  
  561.     n = RowNumber(Curschar);
  562.     AppendPositionToUndobuff(Curschar->index, n);
  563.  
  564.     startinsert(FALSE);
  565.  
  566.     CHANGED;
  567.     break;
  568.  
  569.       case 'I':
  570.     beginline(TRUE);
  571.     ResetBuffers();
  572.     AppendToRedobuff("I");
  573.     goto doINSERTcmd;
  574.     /* FALLTHROUGH */
  575.  
  576.       case 'i':
  577.       case K_INSERT:
  578.     ResetBuffers();
  579.     AppendToRedobuff("i");
  580.  
  581. doINSERTcmd:
  582.     CLEAROP;
  583.  
  584.     n = RowNumber(Curschar);
  585.     AppendPositionToUndobuff(Curschar->index, n);
  586.     AppendPositionToUndoUndobuff(Curschar->index, n);
  587.     AppendToUndoUndobuff("i");
  588.  
  589.     startinsert(FALSE);
  590.  
  591.     CHANGED;
  592.     break;
  593.  
  594.       case 'o':
  595.     CLEAROP;
  596.     ResetBuffers();
  597.  
  598.     n = RowNumber(Curschar);
  599.     AppendToRedobuff("o");
  600.     AppendPositionToUndobuff(Curschar->index, n);
  601.     AppendPositionToUndoUndobuff(Curschar->index, n);
  602.     AppendToUndoUndobuff("o");
  603.  
  604.     if (opencmd(FORWARD, TRUE)) {
  605.         startinsert(TRUE);
  606.  
  607.         CHANGED;
  608.     }
  609.     last_command = 'o';
  610.     break;
  611.  
  612.       case 'O':
  613.     CLEAROP;
  614.     ResetBuffers();
  615.  
  616.     n = RowNumber(Curschar);
  617.     AppendToRedobuff("O");
  618.     AppendPositionToUndobuff(Curschar->index, n);
  619.     AppendPositionToUndoUndobuff(Curschar->index, n);
  620.     AppendToUndoUndobuff("O");
  621.  
  622.     if (opencmd(BACKWARD, TRUE)) {
  623.         startinsert(TRUE);
  624.  
  625.         CHANGED;
  626.     }
  627.     last_command = 'O';
  628.     break;
  629.  
  630.       case 'd':
  631.     if (operator == DELETE)    /* handle 'dd' */
  632.         goto lineop;
  633.     if (Prenum != 0)
  634.         opnum = Prenum;
  635.     startop = *Curschar;
  636.     operator = DELETE;
  637.     break;
  638.  
  639.     /*
  640.      * Some convenient abbreviations... 
  641.      */
  642.  
  643.       case 'x':
  644.     if (Prenum)
  645.         stuffnumReadbuff(Prenum);
  646.     stuffReadbuff("dl");
  647.     break;
  648.  
  649.       case 'X':
  650.     if (Prenum)
  651.         stuffnumReadbuff(Prenum);
  652.     stuffReadbuff("dh");
  653.     break;
  654.  
  655.       case 'D':
  656.     stuffReadbuff("d$");
  657.     break;
  658.  
  659.       case 'Y':
  660.     if (Prenum)
  661.         stuffnumReadbuff(Prenum);
  662.     stuffReadbuff("yy");
  663.     break;
  664.  
  665.       case 'C':
  666.     stuffReadbuff("c$");
  667.     break;
  668.  
  669.       case 'c':
  670.     if (operator == CHANGE) {    /* handle 'cc' */
  671.         CLEAROP;
  672.         stuffReadbuff("0c$");
  673.         break;
  674.     }
  675.     if (Prenum != 0)
  676.         opnum = Prenum;
  677.     startop = *Curschar;
  678.     operator = CHANGE;
  679.     break;
  680.  
  681.       case 'y':
  682.     if (operator == YANK)    /* handle 'yy' */
  683.         goto lineop;
  684.     if (Prenum != 0)
  685.         opnum = Prenum;
  686.     startop = *Curschar;
  687.     operator = YANK;
  688.     break;
  689.  
  690.       case 'p':
  691.     if (RedrawingDisabled) {
  692.         RedrawingDisabled = FALSE;
  693.         updateNextscreen();
  694.         break;
  695.     } else if (Yankbuffptr != NULL) {
  696.         doput(FORWARD);
  697.  
  698.         if (ybcrossline) {
  699.         n = RowNumber(Curschar);
  700.         stuffnumReadbuff(n);
  701.         stuffReadbuff("G");
  702.         if (ybtype == MCHAR) {
  703.             stuffnumReadbuff(Curschar->index);
  704.             if (Curschar->index)
  705.             stuffReadbuff("l");
  706.         } else
  707.             stuffnumReadbuff(0);
  708.         }
  709.         stuffReadbuff("p");
  710.         RedrawingDisabled = TRUE;
  711.     } else
  712.         beep();
  713.     break;
  714.  
  715.       case 'P':
  716.     if (RedrawingDisabled) {
  717.         RedrawingDisabled = FALSE;
  718.         updateNextscreen();
  719.         break;
  720.     } else if (Yankbuffptr != NULL) {
  721.         doput(BACKWARD);
  722.  
  723.         n = RowNumber(Curschar);
  724.         stuffnumReadbuff(n);
  725.         stuffReadbuff("G");
  726.         if (ybtype == MCHAR) {
  727.         stuffnumReadbuff(Curschar->index);
  728.         if (Curschar->index)
  729.             stuffReadbuff("l");
  730.         } else
  731.         stuffnumReadbuff(0);
  732.         stuffReadbuff("P");
  733.         RedrawingDisabled = TRUE;
  734.     } else
  735.         beep();
  736.     break;
  737.  
  738.       case '>':
  739.     if (operator == RSHIFT)    /* handle >> */
  740.         goto lineop;
  741.     if (operator == LSHIFT) {
  742.         CLEAROP;
  743.         beep();
  744.         break;
  745.     }
  746.     if (Prenum != 0)
  747.         opnum = Prenum;
  748.     startop = *Curschar;    /* save current position */
  749.     operator = RSHIFT;
  750.     break;
  751.  
  752.       case '<':
  753.     if (operator == LSHIFT)    /* handle << */
  754.         goto lineop;
  755.     if (operator == RSHIFT) {
  756.         CLEAROP;
  757.         beep();
  758.         break;
  759.     }
  760.     if (Prenum != 0)
  761.         opnum = Prenum;
  762.     startop = *Curschar;    /* save current position */
  763.     operator = LSHIFT;
  764.     break;
  765.  
  766.       case 's':        /* substitute characters */
  767.     if (Prenum)
  768.         stuffnumReadbuff(Prenum);
  769.     stuffReadbuff("cl");
  770.     break;
  771.  
  772.       case '?':
  773.       case '/':
  774.       case ':':
  775.     CLEAROP;
  776.     readcmdline(c, (char *) NULL);
  777.     break;
  778.  
  779.       case 'n':
  780.     mtype = MCHAR;
  781.     mincl = FALSE;
  782.     set_want_col = TRUE;
  783.     repsearch(0);
  784.     break;
  785.  
  786.       case 'N':
  787.     mtype = MCHAR;
  788.     mincl = FALSE;
  789.     set_want_col = TRUE;
  790.     repsearch(1);
  791.     break;
  792.  
  793.     /*
  794.      * Character searches 
  795.      */
  796.       case 'T':
  797.     dir = BACKWARD;
  798.     /* FALLTHROUGH */
  799.  
  800.       case 't':
  801.     type = 1;
  802.     goto docsearch;
  803.  
  804.       case 'F':
  805.     dir = BACKWARD;
  806.     /* FALLTHROUGH */
  807.  
  808.       case 'f':
  809. docsearch:
  810.     mtype = MCHAR;
  811.     mincl = TRUE;
  812.     set_want_col = TRUE;
  813.     if ((nchar = vgetc()) == ESC)    /* search char */
  814.         break;
  815.     if (!searchc(nchar, dir, type)) {
  816.         CLEAROP;
  817.         beep();
  818.     }
  819.     break;
  820.  
  821.       case ',':
  822.     flag = 1;
  823.     /* FALLTHROUGH */
  824.  
  825.       case ';':
  826.     mtype = MCHAR;
  827.     mincl = TRUE;
  828.     set_want_col = TRUE;
  829.     if (!crepsearch(flag)) {
  830.         CLEAROP;
  831.         beep();
  832.     }
  833.     break;
  834.  
  835.     /*
  836.      * Function searches 
  837.      */
  838.  
  839.       case '[':
  840.     dir = BACKWARD;
  841.     /* FALLTHROUGH */
  842.  
  843.       case ']':
  844.     mtype = MLINE;
  845.     set_want_col = TRUE;
  846.     if (vgetc() != c)
  847.         beep();
  848.  
  849.     if (!findfunc(dir)) {
  850.         CLEAROP;
  851.         beep();
  852.     }
  853.     break;
  854.  
  855.     /*
  856.      * Marks 
  857.      */
  858.  
  859.       case 'm':
  860.     CLEAROP;
  861.     if (!setmark(vgetc()))
  862.         beep();
  863.     break;
  864.  
  865.       case '\'':
  866.     flag = TRUE;
  867.     /* FALLTHROUGH */
  868.  
  869.       case '`':
  870.     {
  871.         LPTR            mtmp, *mark = getmark(vgetc());
  872.  
  873.         if (mark == NULL) {
  874.         CLEAROP;
  875.         beep();
  876.         } else {
  877.         mtmp = *mark;
  878.         setpcmark();
  879.         *Curschar = mtmp;
  880.         if (flag)
  881.             beginline(TRUE);
  882.         }
  883.         mtype = flag ? MLINE : MCHAR;
  884.         mincl = TRUE;    /* ignored if not MCHAR */
  885.         set_want_col = TRUE;
  886.     }
  887.     break;
  888.  
  889.       case 'r':
  890.     CLEAROP;
  891.     if (lineempty(Curschar)) {    /* Nothing to replace */
  892.         beep();
  893.         break;
  894.     }
  895.     if ((nchar = vgetc()) == ESC)
  896.         break;
  897.  
  898.     Prenum = DEFAULT1(Prenum);
  899.     n = strlen(Curschar->linep->s) - Curschar->index;
  900.     if (n < Prenum) {
  901.         beep();
  902.         break;
  903.     }
  904.     ResetBuffers();
  905.  
  906.     nn = RowNumber(Curschar);
  907.     AppendPositionToUndobuff(Curschar->index, nn);
  908.     AppendPositionToUndoUndobuff(Curschar->index, nn);
  909.  
  910.     while (Prenum > 0) {
  911.         AppendToRedobuff("r");
  912.         AppendToRedobuff(mkstr(nchar));
  913.  
  914.         AppendToUndobuff("r");
  915.         AppendToUndobuff(mkstr(gchar(Curschar)));
  916.  
  917.         AppendToUndoUndobuff("r");
  918.         AppendToUndoUndobuff(mkstr(nchar));
  919.  
  920.         pchar(Curschar, nchar);    /* Change current character. */
  921.  
  922.         if (Prenum > 1) {
  923.         oneright();
  924.         AppendToRedobuff("l");
  925.         AppendToUndobuff("l");
  926.         AppendToUndoUndobuff("l");
  927.         }
  928.         Prenum--;
  929.     }
  930.  
  931.     CHANGED;
  932.     updateline();
  933.     break;
  934.  
  935.       case '~':        /* swap case */
  936.     CLEAROP;
  937.     if (lineempty(Curschar)) {
  938.         beep();
  939.         break;
  940.     }
  941.     ResetBuffers();
  942.  
  943.     n = RowNumber(Curschar);
  944.     AppendPositionToUndobuff(Curschar->index, n);
  945.     AppendPositionToUndoUndobuff(Curschar->index, n);
  946.  
  947.     Prenum = DEFAULT1(Prenum);
  948.     if (Prenum > 0) {
  949.         AppendNumberToRedobuff(Prenum);
  950.         AppendNumberToUndobuff(Prenum);
  951.         AppendNumberToUndoUndobuff(Prenum);
  952.     }
  953.     AppendToRedobuff("~");
  954.     AppendToUndobuff("~");
  955.     AppendToUndoUndobuff("~");
  956.  
  957.     while (Prenum > 0) {
  958.         c = gchar(Curschar);
  959.         if (isalpha(c)) {
  960.         if (islower(c))
  961.             pchar(Curschar, toupper(c));
  962.         else
  963.             pchar(Curschar, tolower(c));
  964.         }
  965.         if (!oneright())
  966.         break;
  967.         Prenum--;
  968.     }
  969.  
  970.     CHANGED;
  971.     updateline();
  972.     break;
  973.  
  974.       case UNDO_SHIFTJ:
  975.     CLEAROP;
  976.     if (UndoInProgress) {
  977.         if (dojoin(FALSE, FALSE)) {
  978.         CHANGED;
  979.         updateNextscreen();
  980.         }
  981.         break;
  982.     }
  983.     goto doSHIFTJcommand;
  984.  
  985.       case 'J':
  986.     CLEAROP;
  987. doSHIFTJcommand:
  988.     if (nextline(Curschar) == NULL) {    /* on last line */
  989.         beep();
  990.         break;
  991.     }
  992.     ResetBuffers();
  993.  
  994.     temp_Curschar = *Curschar;
  995.     nn = strlen(Curschar->linep->s);
  996.     if (nn < 0)
  997.         nn = 0;
  998.     n = RowNumber(&temp_Curschar);
  999.  
  1000.     AppendToRedobuff("J");
  1001.  
  1002.     AppendPositionToUndobuff(nn, n);
  1003.  
  1004.     AppendPositionToUndoUndobuff(0, n);
  1005.     AppendToUndoUndobuff("J");
  1006.  
  1007.     if (lineempty(nextline(Curschar))) {
  1008.         AppendToUndobuff("a\n");
  1009.         if (!dojoin(FALSE, TRUE)) {
  1010.         beep();
  1011.         break;
  1012.         }
  1013.     } else if (lineempty(Curschar)) {
  1014.         AppendToUndobuff("i\n");
  1015.         if (!dojoin(FALSE, TRUE)) {
  1016.         beep();
  1017.         break;
  1018.         }
  1019.     } else {
  1020.         AppendToUndobuff("dli\n");
  1021.         if (!dojoin(TRUE, TRUE)) {
  1022.         beep();
  1023.         break;
  1024.         }
  1025.     }
  1026.  
  1027.     AppendToUndobuff(ESC_STR);
  1028.     AppendPositionToUndobuff(nn, n);
  1029.  
  1030.     CHANGED;
  1031.     updateNextscreen();
  1032.     break;
  1033.  
  1034.       case K_CGRAVE:        /* shorthand command */
  1035.     CLEAROP;
  1036.     stuffReadbuff(":e #\n");
  1037.     break;
  1038.  
  1039.       case 'Z':        /* write, if changed, and exit */
  1040.     if (vgetc() != 'Z') {
  1041.         beep();
  1042.         break;
  1043.     }
  1044.     if (Changed) {
  1045.         if (Filename != NULL) {
  1046.         if (!writeit(Filename, (LPTR *) NULL, (LPTR *) NULL))
  1047.             return;
  1048.         } else {
  1049.         emsg("No output file");
  1050.         return;
  1051.         }
  1052.     }
  1053.     getout(0);
  1054.     break;
  1055.  
  1056.       case '.':
  1057.     CLEAROP;
  1058.     if (RedrawingDisabled) {
  1059.         RedrawingDisabled = FALSE;
  1060.         updateNextscreen();
  1061.     } else if (Redobuffptr != NULL) {
  1062.         stuffReadbuff(Redobuff);
  1063.         stuffReadbuff(".");
  1064.         RedrawingDisabled = TRUE;
  1065.     } else
  1066.         beep();
  1067.     break;
  1068.  
  1069.       case 'u':
  1070.       case K_UNDO:
  1071.     CLEAROP;
  1072.     if (UndoInProgress) {
  1073.         p = UndoUndobuff;
  1074.         UndoUndobuff = Undobuff;
  1075.         Undobuff = p;
  1076.         p = UndoUndobuffptr;
  1077.         UndoUndobuffptr = Undobuffptr;
  1078.         Undobuffptr = p;
  1079.  
  1080.         UndoInProgress = FALSE;
  1081.         RedrawingDisabled = FALSE;
  1082.         updateNextscreen();
  1083.     } else if (Undobuffptr != NULL) {
  1084.         stuffReadbuff(Undobuff);
  1085.         stuffReadbuff("u");
  1086.         UndoInProgress = TRUE;
  1087.         RedrawingDisabled = TRUE;
  1088.     } else {
  1089.         beep();
  1090.     }
  1091.     break;
  1092.  
  1093.       default:
  1094.     CLEAROP;
  1095.     beep();
  1096.     break;
  1097.     }
  1098.  
  1099.     /*
  1100.      * If an operation is pending, handle it... 
  1101.      */
  1102.     if (finish_op) {        /* we just finished an operator */
  1103.     if (operator == NOP)    /* ... but it was cancelled */
  1104.         return;
  1105.  
  1106.     switch (operator) {
  1107.  
  1108.       case LSHIFT:
  1109.       case RSHIFT:
  1110.         ResetBuffers();
  1111.  
  1112.         n = RowNumber(&startop);
  1113.         AppendPositionToUndobuff(startop.index, n);
  1114.         AppendPositionToUndoUndobuff(startop.index, n);
  1115.         if (Prenum != 0) {
  1116.         AppendNumberToRedobuff(Prenum);
  1117.         AppendNumberToUndobuff(Prenum);
  1118.         AppendNumberToUndoUndobuff(Prenum);
  1119.         }
  1120.         AppendToRedobuff((operator == LSHIFT) ? "<" : ">");
  1121.         AppendToUndobuff((operator == LSHIFT) ? ">" : "<");
  1122.         AppendToUndoUndobuff((operator == LSHIFT) ? "<" : ">");
  1123.         AppendToRedobuff(mkstr(c));
  1124.         if (c == '>')
  1125.         AppendToUndobuff("<");
  1126.         else if (c == '<')
  1127.         AppendToUndobuff(">");
  1128.         else
  1129.         AppendToUndobuff(mkstr(c));
  1130.         AppendToUndoUndobuff(mkstr(c));
  1131.  
  1132.         doshift(operator);
  1133.  
  1134.         CHANGED;
  1135.         break;
  1136.  
  1137.       case DELETE:
  1138.         ResetBuffers();
  1139.  
  1140.         n = RowNumber(&startop);
  1141.         AppendPositionToUndoUndobuff(startop.index, n);
  1142.  
  1143.         temp_Curschar = (lt(&startop, Curschar) ? startop : *Curschar);
  1144.         n = RowNumber(&temp_Curschar);
  1145.         if (Prenum != 0) {
  1146.         AppendNumberToRedobuff(Prenum);
  1147.         AppendNumberToUndoUndobuff(Prenum);
  1148.         }
  1149.         AppendToRedobuff("d");
  1150.         AppendToUndoUndobuff("d");
  1151.         AppendToRedobuff(mkstr(c));
  1152.         AppendToUndoUndobuff(mkstr(c));
  1153.         if (nchar != NUL) {
  1154.         AppendToRedobuff(mkstr(nchar));
  1155.         AppendToUndoUndobuff(mkstr(nchar));
  1156.         }
  1157.         AppendPositionToUndobuff(temp_Curschar.index, n);
  1158.  
  1159.         dodelete(TRUE, TRUE, TRUE);
  1160.  
  1161.         AppendPositionToUndobuff(temp_Curschar.index, n);
  1162.  
  1163.         CHANGED;
  1164.         break;
  1165.  
  1166.       case YANK:
  1167.         ResetBuffers();    /* no redo/undo/(undo of undo) on yank... */
  1168.         doyank();
  1169.         if (!ybcrossline)
  1170.         *Curschar = startop;
  1171.         else if (lt(&startop, Curschar))
  1172.         *Curschar = startop;
  1173.         break;
  1174.  
  1175.       case CHANGE:
  1176.         ResetBuffers();
  1177.  
  1178.         n = RowNumber(&startop);
  1179.         AppendPositionToUndoUndobuff(startop.index, n);
  1180.  
  1181.         temp_Curschar = (lt(&startop, Curschar) ? startop : *Curschar);
  1182.         n = RowNumber(&temp_Curschar);
  1183.         if (mtype == MLINE)
  1184.         AppendPositionToUndobuff(0, n);
  1185.         else
  1186.         AppendPositionToUndobuff(temp_Curschar.index, n);
  1187.  
  1188.         if (Prenum != 0) {
  1189.         AppendNumberToRedobuff(Prenum);
  1190.         AppendNumberToUndoUndobuff(Prenum);
  1191.         }
  1192.         AppendToRedobuff("c");
  1193.         AppendToUndoUndobuff("c");
  1194.         AppendToRedobuff(mkstr(c));
  1195.         AppendToUndoUndobuff(mkstr(c));
  1196.         if (nchar != NUL) {
  1197.         AppendToRedobuff(mkstr(nchar));
  1198.         AppendToUndoUndobuff(mkstr(nchar));
  1199.         }
  1200.         dochange();
  1201.  
  1202.         CHANGED;
  1203.  
  1204.         last_command = 'c';
  1205.         break;
  1206.  
  1207.       default:
  1208.         beep();
  1209.     }
  1210.     operator = NOP;
  1211.     }
  1212. }
  1213.  
  1214. /*
  1215.  * tabinout(shift_type, num) 
  1216.  *
  1217.  * If shift_type == RSHIFT, add a tab to the begining of the next num lines;
  1218.  * otherwise delete a tab from the beginning of the next num lines. 
  1219.  */
  1220. static void
  1221. tabinout(shift_type, num)
  1222.     int             shift_type;
  1223.     int             num;
  1224. {
  1225.     LPTR           *p;
  1226.  
  1227.     beginline(FALSE);
  1228.     while (num-- > 0) {
  1229.     beginline(FALSE);
  1230.     if (shift_type == RSHIFT)
  1231.         inschar(TAB);
  1232.     else {
  1233.         if (gchar(Curschar) == TAB)
  1234.         delchar(TRUE, FALSE);
  1235.     }
  1236.     if (num > 0) {
  1237.         if ((p = nextline(Curschar)) != NULL)
  1238.         *Curschar = *p;
  1239.         else
  1240.         break;
  1241.     }
  1242.     }
  1243. }
  1244.  
  1245. /*
  1246.  * doshift - handle a shift operation 
  1247.  */
  1248. static void
  1249. doshift(op)
  1250.     int             op;
  1251. {
  1252.     LPTR            top, bot;
  1253.     int             nlines;
  1254.  
  1255.     top = startop;
  1256.     bot = *Curschar;
  1257.  
  1258.     if (lt(&bot, &top))
  1259.     pswap(top, bot);
  1260.  
  1261.     nlines = cntllines(&top, &bot);
  1262.     *Curschar = top;
  1263.     tabinout(op, nlines);
  1264.  
  1265.     /*
  1266.      * The cursor position afterward is the prior of the two positions. 
  1267.      */
  1268.     *Curschar = top;
  1269.  
  1270.     /*
  1271.      * If we were on the last char of a line that got shifted left, then move
  1272.      * left one so we aren't beyond the end of the line 
  1273.      */
  1274.     if (gchar(Curschar) == NUL && Curschar->index > 0)
  1275.     Curschar->index--;
  1276.  
  1277.     if (op == RSHIFT)
  1278.     oneright();
  1279.     else
  1280.     oneleft();
  1281.  
  1282.     updateNextscreen();
  1283.  
  1284.     if (nlines > P(P_RP))
  1285.     smsg("%d lines %ced", nlines, (op == RSHIFT) ? '>' : '<');
  1286. }
  1287.  
  1288. /*
  1289.  * dodelete - handle a delete operation 
  1290.  */
  1291. static void
  1292. dodelete(redraw, setup_for_undo, try_to_yank)
  1293.     bool_t          redraw;
  1294.     bool_t          setup_for_undo;
  1295.     bool_t          try_to_yank;
  1296. {
  1297.     LPTR            top, bot;
  1298.     int             nlines;
  1299.     int             n;
  1300.  
  1301.     /*
  1302.      * Do a yank of whatever we're about to delete. If there's too much stuff
  1303.      * to fit in the yank buffer, then get a confirmation before doing the
  1304.      * delete. This is crude, but simple. And it avoids doing a delete of
  1305.      * something we can't put back if we want. 
  1306.      */
  1307.     if (try_to_yank) {
  1308.     if (!doyank()) {
  1309.         msg("yank buffer exceeded: press <y> to confirm");
  1310.         if (vgetc() != 'y') {
  1311.         msg("delete aborted");
  1312.         *Curschar = startop;
  1313.         return;
  1314.         }
  1315.     }
  1316.     }
  1317.     top = startop;
  1318.     bot = *Curschar;
  1319.  
  1320.     if (lt(&bot, &top))
  1321.     pswap(top, bot);
  1322.  
  1323.     *Curschar = top;
  1324.     nlines = cntllines(&top, &bot);
  1325.     cursupdate();
  1326.  
  1327.     if (mtype == MLINE) {
  1328.     if (operator == CHANGE) {
  1329.         last_command_char = 'a';
  1330.         delline(nlines - 1);
  1331.         Curschar->index = 0;
  1332.         while (delchar(TRUE, FALSE));
  1333.     } else {
  1334.         if (bot.linep->next == Fileend->linep)
  1335.         last_command_char = 'o';
  1336.         else
  1337.         last_command_char = 'O';
  1338.         if (setup_for_undo)
  1339.         AppendToUndobuff(mkstr(last_command_char));
  1340.         delline(nlines);
  1341.     }
  1342.     } else if (top.linep == bot.linep) {    /* del. within line */
  1343.     if (!mincl)
  1344.         dec(&bot);
  1345.  
  1346.     if (endofline(&bot))
  1347.         last_command_char = 'a';
  1348.     else
  1349.         last_command_char = 'i';
  1350.     if (setup_for_undo)
  1351.         AppendToUndobuff(mkstr(last_command_char));
  1352.     n = bot.index - top.index + 1;
  1353.     while (n--)
  1354.         if (!delchar(TRUE, FALSE))
  1355.         break;
  1356.     } else {            /* del. between lines */
  1357.     if (endofline(&top)) {
  1358.         if (nextline(&top)) {
  1359.         if (lineempty(nextline(&top)))
  1360.             last_command_char = 'a';
  1361.         else
  1362.             last_command_char = 'i';
  1363.         } else {
  1364.         last_command_char = 'a';
  1365.         }
  1366.     } else {
  1367.         last_command_char = 'i';
  1368.     }
  1369.     if (setup_for_undo)
  1370.         AppendToUndobuff(mkstr(last_command_char));
  1371.  
  1372.     n = Curschar->index;
  1373.     while (Curschar->index >= n)
  1374.         if (!delchar(TRUE, FALSE))
  1375.         break;
  1376.  
  1377.     top = *Curschar;
  1378.     *Curschar = *nextline(Curschar);
  1379.     delline(nlines - 2);
  1380.     Curschar->index = 0;
  1381.     n = bot.index;
  1382.     if (!mincl)
  1383.         n--;
  1384.  
  1385.     while (n-- >= 0)
  1386.         if (!delchar(TRUE, FALSE))
  1387.         break;
  1388.     *Curschar = top;
  1389.     dojoin(FALSE, FALSE);
  1390.     }
  1391.  
  1392.     if (mtype == MCHAR && nlines == 1 && redraw)
  1393.     updateline();
  1394.     else
  1395.     updateNextscreen();
  1396.  
  1397.     if (nlines > P(P_RP))
  1398.     smsg("%d fewer lines", nlines);
  1399.  
  1400.     if (setup_for_undo) {
  1401.     AppendToUndobuff(Yankbuff);
  1402.     AppendToUndobuff(ESC_STR);
  1403.     }
  1404. }
  1405.  
  1406. /*
  1407.  * dochange - handle a change operation 
  1408.  */
  1409. static void
  1410. dochange()
  1411. {
  1412.     bool_t          doappend;    /* true if we should do append, not insert */
  1413.  
  1414.     doappend = lt(Curschar, &startop);    /* needed for some compilers */
  1415.     doappend = endofline(doappend ? &startop : Curschar);
  1416.  
  1417.     dodelete(FALSE, FALSE, TRUE);
  1418.  
  1419.     if (doappend && !lineempty(Curschar))
  1420.     inc(Curschar);
  1421.  
  1422.     startinsert(FALSE);
  1423. }
  1424.  
  1425. static          bool_t
  1426. doyank()
  1427. {
  1428.     LPTR            top, bot;
  1429.     char           *ybend = &Yankbuff[YANKSIZE - 1];
  1430.     int             nlines;
  1431.  
  1432.     Yankbuffptr = Yankbuff;
  1433.  
  1434.     top = startop;
  1435.     bot = *Curschar;
  1436.  
  1437.     if (lt(&bot, &top))
  1438.     pswap(top, bot);
  1439.  
  1440.     nlines = cntllines(&top, &bot);
  1441.  
  1442.     ybtype = mtype;        /* set the yank buffer type */
  1443.     ybcrossline = FALSE;
  1444.     if (LINEOF(&top) != LINEOF(&bot))
  1445.     ybcrossline = TRUE;
  1446.  
  1447.     if (mtype == MLINE) {
  1448.     ybcrossline = TRUE;
  1449.     top.index = 0;
  1450.     bot.index = strlen(bot.linep->s);
  1451.     /*
  1452.      * The following statement checks for the special case of yanking a
  1453.      * blank line at the beginning of the file. If not handled right, we
  1454.      * yank an extra char (a newline). 
  1455.      */
  1456.     if (dec(&bot) == -1) {
  1457.         *Yankbuff = NUL;
  1458.         Yankbuffptr = NULL;
  1459.         return TRUE;
  1460.     }
  1461.     } else {
  1462.     if (!mincl)
  1463.         if (!equal(&top, &bot))
  1464.         dec(&bot);
  1465.     }
  1466.  
  1467.     for (; ltoreq(&top, &bot); inc(&top)) {
  1468.     *Yankbuffptr = (gchar(&top) != NUL) ? gchar(&top) : NL;
  1469.     Yankbuffptr++;
  1470.     if (Yankbuffptr >= ybend) {
  1471.         *Yankbuffptr = NUL;
  1472.         msg("yank too big for buffer");
  1473.         ybtype = MBAD;
  1474.         return FALSE;
  1475.     }
  1476.     }
  1477.  
  1478.     *Yankbuffptr = NUL;
  1479.  
  1480.     if (operator == YANK)
  1481.     if (nlines > P(P_RP))
  1482.         smsg("%d lines yanked", nlines);
  1483.  
  1484.     return TRUE;
  1485. }
  1486.  
  1487. static void
  1488. doput(dir)
  1489.     int             dir;
  1490. {
  1491.     bool_t          type;
  1492.  
  1493.     if (ybtype == MBAD) {
  1494.     beep();
  1495.     return;
  1496.     }
  1497.     type = (ybtype == MCHAR);
  1498.     if (dir == FORWARD)
  1499.     stuffReadbuff(type ? "a" : "o");
  1500.     else
  1501.     stuffReadbuff(type ? "i" : "O");
  1502.  
  1503.     stuffReadbuff(Yankbuff);
  1504.     stuffReadbuff(ESC_STR);
  1505. }
  1506.  
  1507. static void
  1508. startinsert(startln)
  1509.     int             startln;    /* if set, insert at start of line */
  1510. {
  1511.     *Insstart = *Curschar;
  1512.     if (startln) {
  1513.     Insstart->index = 0;
  1514.     }
  1515.     Insbuffptr = Insbuff;
  1516.     *Insbuffptr = NUL;
  1517.     State = INSERT;
  1518.     if (P(P_MO))
  1519.     msg("Insert Mode");
  1520. }
  1521.  
  1522. void
  1523. ResetBuffers()
  1524. {
  1525.     if (UndoInProgress)
  1526.     return;
  1527.  
  1528.     *Redobuff = NUL;
  1529.     Redobuffptr = NULL;
  1530.  
  1531.     *Undobuff = NUL;
  1532.     Undobuffptr = NULL;
  1533.  
  1534.     *UndoUndobuff = NUL;
  1535.     UndoUndobuffptr = NULL;
  1536. }
  1537.  
  1538. void
  1539. AppendToRedobuff(s)
  1540.     char           *s;
  1541. {
  1542.     if (UndoInProgress)
  1543.     return;
  1544.  
  1545.     if (Redobuffptr == NULL) {
  1546.     if ((strlen(s) + 1) < REDO_UNDO_SIZE) {
  1547.         strcpy(Redobuff, s);
  1548.         Redobuffptr = Redobuff;
  1549.     } else
  1550.         emsg("Couldn't AppendToRedobuff() - should never happen");
  1551.     return;
  1552.     }
  1553.     if ((strlen(Redobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) {
  1554.     strcat(Redobuff, s);
  1555.     return;
  1556.     }
  1557.     emsg("Couldn't AppendToRedobuff() - should never happen");
  1558. }
  1559.  
  1560. void
  1561. AppendNumberToRedobuff(n)
  1562.     int             n;
  1563. {
  1564.     char            buf[32];
  1565.  
  1566.     if (UndoInProgress)
  1567.     return;
  1568.  
  1569.     sprintf(buf, "%d", n);
  1570.     AppendToRedobuff(buf);
  1571. }
  1572.  
  1573. void
  1574. AppendToUndobuff(s)
  1575.     char           *s;
  1576. {
  1577.     if (UndoInProgress)
  1578.     return;
  1579.  
  1580.     if (Undobuffptr == NULL) {
  1581.     if ((strlen(s) + 1) < REDO_UNDO_SIZE) {
  1582.         strcpy(Undobuff, s);
  1583.         Undobuffptr = Undobuff;
  1584.     } else
  1585.         emsg("Couldn't AppendToUndobuff() - should never happen");
  1586.     return;
  1587.     }
  1588.     if ((strlen(Undobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) {
  1589.     strcat(Undobuff, s);
  1590.     return;
  1591.     }
  1592.     emsg("Couldn't AppendToUndobuff() - should never happen");
  1593. }
  1594.  
  1595. void
  1596. AppendNumberToUndobuff(n)
  1597.     int             n;
  1598. {
  1599.     char            buf[32];
  1600.  
  1601.     if (UndoInProgress)
  1602.     return;
  1603.  
  1604.     sprintf(buf, "%d", n);
  1605.     AppendToUndobuff(buf);
  1606. }
  1607.  
  1608. void
  1609. AppendPositionToUndobuff(column, row)
  1610.     int             column;
  1611.     int             row;
  1612. {
  1613.     if (UndoInProgress)
  1614.     return;
  1615.  
  1616.     AppendNumberToUndobuff(row);
  1617.     AppendToUndobuff("G");
  1618.     AppendNumberToUndobuff(column);
  1619.     if (column)
  1620.     AppendToUndobuff("l");
  1621. }
  1622.  
  1623. void
  1624. AppendToUndoUndobuff(s)
  1625.     char           *s;
  1626. {
  1627.     if (UndoInProgress)
  1628.     return;
  1629.  
  1630.     if (UndoUndobuffptr == NULL) {
  1631.     if ((strlen(s) + 1) < REDO_UNDO_SIZE) {
  1632.         strcpy(UndoUndobuff, s);
  1633.         UndoUndobuffptr = Undobuff;
  1634.     } else
  1635.         emsg("Couldn't AppendToUndoUndobuff() - should never happen");
  1636.     return;
  1637.     }
  1638.     if ((strlen(UndoUndobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) {
  1639.     strcat(UndoUndobuff, s);
  1640.     return;
  1641.     }
  1642.     emsg("Couldn't AppendToUndoUndobuff() - should never happen");
  1643. }
  1644.  
  1645. void
  1646. AppendNumberToUndoUndobuff(n)
  1647.     int             n;
  1648. {
  1649.     char            buf[32];
  1650.  
  1651.     if (UndoInProgress)
  1652.     return;
  1653.  
  1654.     sprintf(buf, "%d", n);
  1655.     AppendToUndoUndobuff(buf);
  1656. }
  1657.  
  1658. void
  1659. AppendPositionToUndoUndobuff(column, row)
  1660.     int             column;
  1661.     int             row;
  1662. {
  1663.     if (UndoInProgress)
  1664.     return;
  1665.  
  1666.     AppendNumberToUndoUndobuff(row);
  1667.     AppendToUndoUndobuff("G");
  1668.     AppendNumberToUndoUndobuff(column);
  1669.     if (column)
  1670.     AppendToUndoUndobuff("l");
  1671. }
  1672.  
  1673. static          bool_t
  1674. dojoin(leading_space, strip_leading_spaces)
  1675.     bool_t          leading_space;
  1676.     bool_t          strip_leading_spaces;
  1677. {
  1678.     int             scol;    /* save cursor column */
  1679.     int             currsize;    /* size of the current line */
  1680.     int             nextsize;    /* size of the next line */
  1681.  
  1682.     if (nextline(Curschar) == NULL)    /* on last line */
  1683.     return FALSE;
  1684.  
  1685.     if (!canincrease(nextsize = strlen(Curschar->linep->next->s)))
  1686.     return FALSE;
  1687.  
  1688.     currsize = strlen(Curschar->linep->s);
  1689.  
  1690.     while (oneright());        /* to end of line */
  1691.  
  1692.     strcat(Curschar->linep->s, Curschar->linep->next->s);
  1693.  
  1694.     /*
  1695.      * Delete the following line. To do this we move the cursor there
  1696.      * briefly, and then move it back. Don't back up if the delete made us
  1697.      * the last line. 
  1698.      */
  1699.     Curschar->linep = Curschar->linep->next;
  1700.     scol = Curschar->index;
  1701.  
  1702.     if (nextline(Curschar) != NULL) {
  1703.     delline(1);
  1704.     Curschar->linep = Curschar->linep->prev;
  1705.     } else
  1706.     delline(1);
  1707.  
  1708.     Curschar->index = scol;
  1709.  
  1710.     if (currsize)
  1711.     oneright();        /* go to first char. of joined line */
  1712.  
  1713.     if (nextsize != 0 && strip_leading_spaces) {
  1714.     /*
  1715.      * Delete leading white space on the joined line and insert a single
  1716.      * space. 
  1717.      */
  1718.     while (gchar(Curschar) == ' ' || gchar(Curschar) == TAB) {
  1719.         delchar(TRUE, TRUE);
  1720.     }
  1721.     if (leading_space)
  1722.         inschar(' ');
  1723.     }
  1724.     return TRUE;
  1725. }
  1726.  
  1727. char           *
  1728. mkstr(c)
  1729.     char            c;
  1730. {
  1731.     static char     s[2];
  1732.  
  1733.     s[0] = c;
  1734.     s[1] = NUL;
  1735.  
  1736.     return s;
  1737. }
  1738.